using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Linq;
using MongoDB.Driver.Linq;
using MongoDB.Driver;
using BlueLotus7.SSQAnalyzeWebService.Properties;
using BlueLotus7.SSQAnalysisCore;
using System.Collections.ObjectModel;
using MongoDB.Driver.Builders;
using System.Xml.Linq;

namespace BlueLotus7
{
    public class SSQLotteryDatabaseService
    {
        protected const string collectionSSQLottery = "ssqLottery";
        protected const string collectionBallMartix = "ballMartix";
        protected const string collectionRelevanceMartix = "relevanceMartix";
        protected const string collectionAnalyzeReport = "analyzeReport";

        protected static object lockerSSQLottery = new object();
        protected static object lockerAnalyzeReport = new object();

        protected const string column_LotteryIdentity = "LotteryIdentity";


        #region Properties

        protected MongoOperator mongoOperator;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        public SSQLotteryDatabaseService()
        {
            this.mongoOperator = new MongoOperator("ssqdb");
        }

        #endregion


        #region Query operations

        public List<SSQLotteryStage> QuerySSQLotteryStages(string startIdentity, string endIdentity)
        {
            var sortedCollection = GetSortedSSQLotteryStages();
            if (sortedCollection.Count > 0)
            {
                if (string.IsNullOrWhiteSpace(startIdentity))
                {
                    startIdentity = sortedCollection.FirstOrDefault().LotteryIdentity;
                }

                if (string.IsNullOrWhiteSpace(endIdentity))
                {
                    endIdentity = sortedCollection.LastOrDefault().LotteryIdentity;
                }

                var result = sortedCollection.Where(
                       one =>
                           startIdentity.CompareTo(one.LotteryIdentity) <= 0 && endIdentity.CompareTo(one.LotteryIdentity) >= 0);
                return result.ToList();
            }

            return (new List<SSQLotteryStage>());
        }

        /// <summary>
        /// Query SSQ martix
        /// </summary>
        /// <param name="startIdentity"></param>
        /// <param name="endIdentity"></param>
        /// <returns></returns>
        public ReadOnlyCollection<SSQMartixItem> QuerySSQMartix(string startIdentity, string endIdentity)
        {
            var collection = this.GetBallMartixCollection();
            var sortedCollection = collection.FindAll().AsQueryable().OrderBy(one => one.LotteryIdentity).ToList();

            if (sortedCollection.Count > 0)
            {
                if (string.IsNullOrWhiteSpace(startIdentity))
                {
                    startIdentity = sortedCollection.FirstOrDefault().LotteryIdentity;
                }

                if (string.IsNullOrWhiteSpace(endIdentity))
                {
                    endIdentity = sortedCollection.LastOrDefault().LotteryIdentity;
                }

                var result = sortedCollection.Where(
                       one =>
                           startIdentity.CompareTo(one.LotteryIdentity) <= 0 && endIdentity.CompareTo(one.LotteryIdentity) >= 0);
                return result.ToList().AsReadOnly();
            }

            return (new List<SSQMartixItem>()).AsReadOnly();
        }

        /// <summary>
        /// Gets <c>AnalysisResult</c> instance by start & end identity.
        /// </summary>
        /// <param name="startIdentity"></param>
        /// <param name="endIdentity"></param>
        /// <returns></returns>
        public AnalysisResult GetAnalysisResultReport(string startIdentity, string endIdentity)
        {
            AnalysisResult result = null;
            MongoCollection<AnalysisResult> collection = GetAnalyzeReportCollection();

            lock (lockerAnalyzeReport)
            {
                if (!string.IsNullOrWhiteSpace(startIdentity) && !string.IsNullOrWhiteSpace(endIdentity))
                {
                    var criteria = Query.And(Query.EQ("CatalogueIdentityStart", startIdentity), Query.EQ("CatalogueIdentityEnd", endIdentity));
                    result = collection.FindOne(criteria);
                }

                if (result == null)
                {
                    var stages = QuerySSQLotteryStages(startIdentity, endIdentity);
                    result = Analyzer.Analyze(stages);
                    collection.Insert(result);
                }
            }

            return result;
        }

        #endregion

        #region Insert or Update operations

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataXml"></param>
        /// <param name="fullySync"></param>
        /// <returns></returns>
        public AnalysisResult SyncLotteryHistoryData(XDocument dataXml, bool fullySync = false)
        {
            AnalysisResult result = new AnalysisResult();

            if (dataXml != null)
            {
                List<SSQLotteryStage> stages = SSQLotteryStage.ConvertSSQLottery(dataXml);
                stages.SortByIdentity();

                MongoCollection<SSQLotteryStage> collection = GetSSQLotteryStageCollection();
                MongoCollection<SSQMartixItem> ballMartixCollection = GetBallMartixCollection();
                result = Analyzer.Analyze(stages);

                lock (lockerSSQLottery)
                {
                    if (!fullySync)
                    {
                        SSQLotteryStage lastStage = collection.AsQueryable().OrderByDescending(one => one.LotteryIdentity).SingleOrDefault();
                        if (lastStage == null)
                        {
                            fullySync = true;
                        }
                        else
                        {
                            SSQMartixItem lastMartixItem = ballMartixCollection.FindOne(Query.EQ(column_LotteryIdentity, lastStage.LotteryIdentity));
                            if (lastMartixItem == null)
                            {
                                fullySync = true;
                            }
                            else
                            {
                                foreach (var one in stages)
                                {
                                    if (lastStage == null || lastStage.LotteryIdentity.CompareTo(one.LotteryIdentity) < 0)
                                    {
                                        collection.Insert<SSQLotteryStage>(one);
                                    }
                                }
                            }
                        }
                    }

                    if (fullySync)
                    {
                        collection.RemoveAll(SafeMode.True);
                        collection.InsertBatch(stages);

                        ballMartixCollection.RemoveAll(SafeMode.True);
                        ballMartixCollection.InsertBatch(result.SSQMartix);
                    }
                }

                SyncAnalysisData(result);
            }
            return result;
        }

        #endregion

        protected List<SSQLotteryStage> GetSortedSSQLotteryStages()
        {
            MongoCollection<SSQLotteryStage> collection = GetSSQLotteryStageCollection();
            return collection.AsQueryable().OrderBy(one => one.LotteryIdentity).ToList();
        }

        protected void SyncAnalysisData(AnalysisResult analysisResult)
        {
            if (analysisResult != null)
            {
                MongoCollection<AnalysisResult> analysisResultCollection = GetAnalyzeReportCollection();

                lock (lockerAnalyzeReport)
                {
                    var criteria = Query.And(Query.EQ("CatalogueIdentityStart", analysisResult.CatalogueIdentityStart), Query.EQ("CatalogueIdentityEnd", analysisResult.CatalogueIdentityEnd));
                    var existingAnalysisResult = analysisResultCollection.FindOne(criteria);

                    if (existingAnalysisResult == null)
                    {
                        analysisResultCollection.Insert(analysisResult);
                    }
                }
            }
        }

        #region Get Collections

        protected MongoCollection<SSQLotteryStage> GetSSQLotteryStageCollection()
        {
            return this.mongoOperator.Database.GetCollection<SSQLotteryStage>(collectionSSQLottery);
        }

        protected MongoCollection<SSQMartixItem> GetBallMartixCollection()
        {
            return this.mongoOperator.Database.GetCollection<SSQMartixItem>(collectionBallMartix);
        }

        protected MongoCollection<SSQLotteryData> GetRelevanceMartixCollection()
        {
            return this.mongoOperator.Database.GetCollection<SSQLotteryData>(collectionRelevanceMartix);
        }

        protected MongoCollection<AnalysisResult> GetAnalyzeReportCollection()
        {
            return this.mongoOperator.Database.GetCollection<AnalysisResult>(collectionAnalyzeReport);
        }

        #endregion
    }
}
